﻿using Baidu.Push.Api.Parser;
using Baidu.Push.Api.Util;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;

namespace Baidu.Push.Api
{
    public class DefaultPushClient : IPushClient
    {
        private const string SERVER_URL = "https://channel.api.duapp.com/rest/2.0/channel/";
        private const string CHANNEL = "channel";
        private const string API_KEY = "apikey";
        private const string METHOD = "method";
        private const string TIMESTAMP = "timestamp";
        private const string VERSION = "v";
        private const string SIGN = "sign";
        private const string EXPIRES = "expires";

        /// <summary>
        /// 禁用响应结果解释
        /// </summary>
        public bool DisableParser { private get; set; }

        /// <summary>
        /// 禁用日志调试功能
        /// </summary>
        public bool DisableTrace { private get; set; }

        public string ServerUrl { get; private set; }
        public IPushLogger PushLogger { get; set; }

        private string apiKey;
        private string appSecret;
        private WebUtils webUtils;

        #region DefaultPushClient Constructors

        public DefaultPushClient(string appKey, string appSecret) : this(SERVER_URL, appKey, appSecret) { }

        public DefaultPushClient(string serverUrl, string appKey, string appSecret)
        {
            this.ServerUrl = serverUrl;
            this.apiKey = appKey;
            this.appSecret = appSecret;
            this.webUtils = new WebUtils();
            this.PushLogger = new DefaultPushLogger();
        }

        #endregion

        #region IPushClient

        public T Execute<T>(IPushRequest<T> request) where T : PushResponse
        {
            return Execute(request, CHANNEL, DateTime.UtcNow);
        }

        public T Execute<T>(IPushRequest<T> request, string resource) where T : PushResponse
        {
            return Execute(request, resource, DateTime.UtcNow);
        }

        public T Execute<T>(IPushRequest<T> request, string resource, DateTime timestamp) where T : PushResponse
        {
            try
            {
                return DoExecute<T>(request, resource, timestamp, null);
            }
            catch (Exception ex)
            {
                if (!DisableTrace) PushLogger.Error(this.ServerUrl + resource + "\r\n" + ex.StackTrace);
                return createErrorResponse<T>(1, ex.Message);
            }
        }

        #endregion

        private T DoExecute<T>(IPushRequest<T> request, string resource, DateTime timestamp, DateTime? expires) where T : PushResponse
        {
            try
            {
                request.Validate();
            }
            catch (PushException e)
            {
                return createErrorResponse<T>(e.ErrorCode, e.ErrorMsg);
            }

            string url = this.ServerUrl + resource;

            var txtParams = new PushDictionary(request.GetParameters());
            txtParams.Add(METHOD, request.GetApiName());
            txtParams.Add(API_KEY, apiKey);
            txtParams.Add(TIMESTAMP, PushUtils.GetUnixTimestamp(timestamp).ToString());
            if (expires.HasValue) txtParams.Add(EXPIRES, PushUtils.GetUnixTimestamp(expires.Value).ToString());
            txtParams.Add(SIGN, PushUtils.SignTopRequest(txtParams, url, this.appSecret));

            string body = webUtils.DoPost(url, txtParams);

            // 解释响应结果
            T rsp = this.DisableParser ? Activator.CreateInstance<T>() : PushUtils.ParseResponse<T>(body);
            rsp.Body = body;

            // 追踪错误的请求
            if (!DisableTrace)
            {
                txtParams.Remove(SIGN);
                txtParams.Add(SIGN, PushUtils.SignTopRequest(txtParams, url, this.appSecret, "GET"));
                rsp.ReqUrl = WebUtils.BuildGetUrl(url, txtParams);
                if (rsp.IsError)
                {
                    PushLogger.Warn(rsp.ReqUrl + "\r\n" + rsp.Body);
                }
            }

            return rsp;
        }

        private static T createErrorResponse<T>(int errCode, string errMsg) where T : PushResponse
        {
            T rsp = Activator.CreateInstance<T>();
            rsp.ErrorCode = errCode;
            rsp.ErrorMsg = errMsg;

            rsp.Body = PushUtils.CreateErrorJson(errCode, errMsg);
            return rsp;
        }
    }
}
